﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace GTA
{
    public class Player
    {
        public Player(Game game, Background background, List<Building> buildingList)
        {
            this.background = background;
            this.buildingList = buildingList;
            this.game = game;
        }
        #region Variables
        Background background;
        List<Building> buildingList;
        Game game;

        Vector2 dist;
        int test = 0;

        // Texture of the player
        Texture2D texture;
        Texture2D rectDummy;
        Vector2 dummyPosition;

        // Position of the player
        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }
        Vector2 position;

        public Vector2 PreviousPosition
        {
            get { return previousPosition; }
            set { previousPosition = value; }
        }
        Vector2 previousPosition;

        // Orientation of the player
        public double Angle
        {
            get { return angle; }
            set { angle = value; }
        }
        double angle;

        public double DegreAngle
        {
            get { return degreAngle; }
            set { degreAngle = value; }
        }
        double degreAngle;

        // Size of the texture
        public Point Size
        {
            get { return size; }
            set { size = value; }
        }
        Point size;

        // Rectangle of the player, to detect collision
        public Rectangle Rect
        {
            get { return rect; }
            set { rect = value; }
        }
        Rectangle rect;

        public float Speed
        {
            get { return speed; }
            set { speed = value; }
        }
        float speed;

        public float MaxSpeed
        {
            get { return maxSpeed; }
            set { maxSpeed = value; }
        }
        float maxSpeed;

        public float Acceleration
        {
            get { return acceleration; }
            set { acceleration = value; }
        }
        float acceleration;

        public float MaxAngularSpeed
        {
            get { return maxAngularSpeed; }
            set { maxAngularSpeed = value; }
        }
        float maxAngularSpeed;

        public float AngularSpeed
        {
            get { return angularSpeed; }
            set { angularSpeed = value; }
        }
        float angularSpeed;

        public int HitPoint
        {
            get { return hitPoint; }
            set { hitPoint = value; }
        }
        int hitPoint;


        // Player state
        public enum PlayerState
        {
            Normal,
            Driving
        }

        public PlayerState State
        {
            get { return state; }
            set { state = value; }
        }
        PlayerState state;

        public enum PlayerDirection
        {
            MoveOn,
            MoveBack,
            None
        }

        public PlayerDirection Direction
        {
            get { return direction; }
            set { direction = value; }
        }
        PlayerDirection direction;

        // Keys 
        public static Keys keyRight = Keys.Right, keyLeft = Keys.Left, keyUp = Keys.Up, keyDown = Keys.Down, keyUse = Keys.E, keyShoot = Keys.Enter;

        /* Texture properties */
        Point frameNumber = new Point(11, 0), currentFrame = new Point(0, 0);
        Rectangle selectedFrame;
        Vector2 originPlayer, originCar;
        int fps = 5, animationTimer = 0;
 
        // Avoids key spamming
        bool canBack = true;
        bool canUse = true;
        bool stopAll = false; // 

        // Center position of the character in the game screen
        Vector2 centerPosition;

        // Fluider scrolling
        int backgroundMargin = 10;

        Car car;
        Weapon weapon;
        #endregion

        // Main Initialization function
        public void InitPlayer(ContentManager Content)
        {
            texture = Content.Load<Texture2D>("Player/player");
            Rectangle selectedFrame = new Rectangle(0, 0, size.X, size.Y);
            car = new Car(this, 18, 0.4f, 0.055f);
            weapon = new Weapon(Content);
            car.InitCar(Content);
            ResetSettings();
            
            // Debug
            SetDummy();

        }

        // Main update function
        public void Update(Background background)
        {
            UpdateAnimation();
            UpdateBooleans();
            KeyControls();

            EnterCar();
            LeaveCar();

            FightUpdate();

            previousPosition = position;
        }

        // Main drawing function
        public void DrawPlayer(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(rectDummy, dummyPosition, Color.White);
            if (state == PlayerState.Normal)
            {
                weapon.Draw(spriteBatch, position + new Vector2(10, -5), (float)angle);
                spriteBatch.Draw(texture, position, selectedFrame, Color.White, (float)angle, originPlayer, 1f, SpriteEffects.None, 0);
            }
            else if (state == PlayerState.Driving)
                spriteBatch.Draw(car.Texture, position, null, Color.White, (float)angle, originCar, 1f, SpriteEffects.None, 0);


            // Debug
            spriteBatch.DrawString(Game1.font, "Player Position : " + position, Vector2.Zero, Color.Black);
            spriteBatch.DrawString(Game1.font, "Background Position : " + background.Position, new Vector2(0, 20), Color.Black);

            spriteBatch.DrawString(Game1.font, "Angle (rad): " + angle, new Vector2(0, 60), Color.Black);
            spriteBatch.DrawString(Game1.font, "Angle (degre): " + degreAngle, new Vector2(0, 80), Color.Black);
            spriteBatch.DrawString(Game1.font, "Last distance sent : " + dist, new Vector2(0, 100), Color.Black);
            spriteBatch.DrawString(Game1.font, "currentFrame : " + currentFrame, new Vector2(0, 120), Color.Black);
            spriteBatch.DrawString(Game1.font, "frameNumber : " + frameNumber, new Vector2(0, 140), Color.Black);
            spriteBatch.DrawString(Game1.font, "animationTimer : " + animationTimer, new Vector2(0, 160), Color.Black);

            spriteBatch.DrawString(Game1.font, "state :" + state, new Vector2(0, 200), Color.Black);
            spriteBatch.DrawString(Game1.font, "speed :" + speed, new Vector2(0, 220), Color.Black);
            spriteBatch.DrawString(Game1.font, "maxspeed ;" + maxSpeed, new Vector2(0, 240), Color.Black);
            spriteBatch.DrawString(Game1.font, "angularSpeed :" + angularSpeed, new Vector2(0, 260), Color.Black);
            spriteBatch.DrawString(Game1.font, "maxangularSpeed :" + maxAngularSpeed, new Vector2(0, 280), Color.Black);
            spriteBatch.DrawString(Game1.font, "Direction :" + direction, new Vector2(0, 300), Color.Black);
            spriteBatch.DrawString(Game1.font, "canBack :" + canBack, new Vector2(0, 320), Color.Black);
            spriteBatch.DrawString(Game1.font, "test :" + test, new Vector2(0, 340), Color.Black);
        }

        /* Custom functions */

        // Do the action depending of the key pressed
        #region Movements
        private void KeyControls()
        {

            Rotations();
            UpdateValues();
            ValuesCorrection();
            MovingOn();
            MovingBack();
            InertiaEffect(); 
        }

        // KeyControl functions
        private void Rotations()
        {
            if (Keyboard.GetState().IsKeyDown(keyLeft))
            {
                angle = (angle - angularSpeed) % (2 * Math.PI);
            }
            if (Keyboard.GetState().IsKeyDown(keyRight))
            {
                angle = (angle + angularSpeed) % (2 * Math.PI);
            }
        }
        private void ValuesCorrection()
        {
            if (speed == 0)
            {
                if(state == PlayerState.Driving)
                    angularSpeed = 0;

                direction = PlayerDirection.None;
            }

            if (angularSpeed > maxAngularSpeed)
                angularSpeed = maxAngularSpeed;
            if (direction == PlayerDirection.MoveOn && speed > maxSpeed)
                speed = maxSpeed;
            if (direction == PlayerDirection.MoveBack && speed < -maxSpeed)
                speed = -maxSpeed;

        }
        private void UpdateValues()
        {
            // Refresh the distance the player has done (& angle)
            dist = new Vector2(-(float)(speed * Math.Cos(angle)), -(float)(speed * Math.Sin(angle)));
            degreAngle = (180 * angle) / Math.PI;
        }
        private void MovingOn()
        {
            if (state == PlayerState.Driving && Keyboard.GetState().IsKeyDown(keyDown))
                return;

            // Front Movements
            if (Keyboard.GetState().IsKeyDown(keyUp))
            {
                if (speed < maxSpeed)
                {
                    speed += acceleration;

                    // The car gains angularSpeed with it's own speed
                    if (state == PlayerState.Driving)
                        angularSpeed = (speed / maxSpeed) * maxAngularSpeed;
                }
                direction = PlayerDirection.MoveOn;
                UpdateWorld(dist);
            }
        }
        private void MovingBack()
        {
            // Back movement (1)
            if (state == PlayerState.Normal && canBack && Keyboard.GetState().IsKeyDown(keyDown))
            {
                degreAngle = (degreAngle + 180) % 360;
                angle = (angle + Math.PI) % (2 * Math.PI);
                canBack = false;
                speed = 0;
                direction = PlayerDirection.MoveBack;

            }
            // Back movement (2)
            else if (state == PlayerState.Normal && !canBack && Keyboard.GetState().IsKeyDown(keyDown))
            {
                if (speed < maxSpeed)
                    speed += acceleration;
                else
                    speed = maxSpeed;
                UpdateWorld(dist);
                direction = PlayerDirection.MoveBack;
            }
            // Car back movement
            else if (Keyboard.GetState().IsKeyUp(keyUp) && state == PlayerState.Driving && Keyboard.GetState().IsKeyDown(keyDown))
            {
                test = 5;
                if (speed <= -maxSpeed)
                    speed = -maxSpeed;
                else
                {
                    speed -= acceleration;

                    // The car gains angularSpeed with it's own speed
                    if (state == PlayerState.Driving)
                        angularSpeed = (-speed / maxSpeed) * maxAngularSpeed;
                }
                
                direction = PlayerDirection.MoveBack;
                UpdateWorld(dist);
            }
        }
        private void InertiaEffect()
        {
            if (Keyboard.GetState().IsKeyUp(keyUp) && Keyboard.GetState().IsKeyUp(keyDown))
            {
                if (direction == PlayerDirection.MoveOn)
                {
                    if (speed < 0)
                    {
                        speed = 0;
                        direction = PlayerDirection.None;
                    }
                    else
                        speed -= acceleration;
                }
                else if (direction == PlayerDirection.MoveBack)
                {
                    if (speed < 0)
                    {
                        speed += acceleration;
                        if (speed > 0)
                        {
                            direction = PlayerDirection.None;
                            speed = 0;
                        }
                    }
                    else
                        speed -= acceleration;
                }

                if (state == PlayerState.Driving)
                    angularSpeed = (speed / maxSpeed) * maxAngularSpeed;


                UpdateWorld(dist);
            }
        }

        // Scrolls all game elements
        private void UpdateWorld(Vector2 distance)
        {
            // Debug
            dist = distance;

            // Stop scrolling
            foreach (Building item in buildingList)
                item.Position += distance;
            background.Position += distance;
 
        }

        // Character jump function
        private void Jumping()
        {

        }

        // Enter/Leave car
        public void EnterCar()
        {
            if (!canUse)
                return;

            if (state == PlayerState.Normal)
            {
                if (Keyboard.GetState().IsKeyDown(keyUse))
                {
                    state = PlayerState.Driving;
                    SetCarSetting();
                    canUse = false;
                }
            }
        }
        public void LeaveCar()
        {
            if (!canUse)
                return;

            if (state == PlayerState.Driving)
            {
                if (Keyboard.GetState().IsKeyDown(keyUse))
                {
                    state = PlayerState.Normal;
                    ResetSettings();
                    canUse = false;
                }
            }
        }
        #endregion

        #region Point of view fonctions
        private bool IsLookingTopRight()
        {
            return ((degreAngle < 0 && degreAngle > -90) || (degreAngle > 270 && degreAngle < 360));
        }
        private bool IsLookingTopLeft()
        {
            return ((degreAngle < -90 && degreAngle > -180) || (degreAngle < 270 && degreAngle > 180));
        }
        private bool IsLookingBottomLeft()
        {
            return ((degreAngle > 90 && degreAngle < 180) || (degreAngle < -180 && degreAngle > -270));
        }
        private bool IsLookingBottomRight()
        {
            return ((degreAngle > 0 && degreAngle < 90) || (degreAngle < -270 && degreAngle > -360));
        }

        private bool IsLookingTop()
        {
            return ((degreAngle < 0 && degreAngle > -180) || (degreAngle > 180 && degreAngle < 360));
        }
        private bool IsLookingLeft()
        {
            return ((degreAngle < 270 && degreAngle > 90) || (degreAngle < -90 && degreAngle > -270));
        }
        private bool IsLookingBottom()
        {
            return ((degreAngle > 0 && degreAngle < 180) || (degreAngle < -180 && degreAngle > -360));
        }
        private bool IsLookingRight()
        {
            return ((degreAngle > -90 && degreAngle < 90) || (degreAngle <= -270 && degreAngle >= -360 || (degreAngle >= 270 && degreAngle <= 360)));
        }
        #endregion

        #region Animation
        private void UpdateAnimation()
        {
            animationTimer++;

            if (animationTimer > fps)
            {
                animationTimer = 0;
                ++currentFrame.X;

                if (currentFrame.X > frameNumber.X - 1)
                {
                    currentFrame.X = 0;
                }
            }
            selectedFrame = new Rectangle(currentFrame.X * size.X, currentFrame.Y * size.Y, size.X, size.Y);
        }
        #endregion

        #region Other
        private void UpdateBooleans()
        {
            if (Keyboard.GetState().IsKeyUp(keyDown))
                canBack = true;
            if (Keyboard.GetState().IsKeyUp(keyUse))
                canUse = true;
        }

        private void ResetSettings()
        {
            speed = 0;
            maxAngularSpeed = 0.085f;
            angularSpeed = maxAngularSpeed;
            maxSpeed = 8;
            size = new Point (80, 80);
            hitPoint = 100;
            position = new Vector2 (Game1.WIDTH / 2 - size.X / 2, Game1.HEIGHT / 2 - size.Y / 2);
            centerPosition = new Vector2 (Game1.WIDTH / 2 - size.X / 2, Game1.HEIGHT / 2 - size.Y / 2);
            direction = PlayerDirection.None;
            state = PlayerState.Normal;
            acceleration = maxSpeed / 12;
            angle = 0;
            this.rect = new Rectangle((int)position.X, (int)position.Y, size.X, size.Y);
            originPlayer = new Vector2(size.X / 2, size.Y / 2);
            originCar = new Vector2(car.Size.X / 2, car.Size.Y / 2);
        }

        private void SetCarSetting()
        {
            maxAngularSpeed = car.AngularSpeed;
            maxSpeed = car.Speed;
            acceleration = car.Accelation;
        }

        #endregion

        #region Fight
        private void FightUpdate()
        {
            weapon.CreatBullet(position, angle);
            weapon.Update();
        }

        #endregion

        #region Collisions
        // Draw a rectangle behind the character
        private void SetDummy()
        {
            dummyPosition = position - originPlayer;
            rectDummy = new Texture2D(game.GraphicsDevice, size.X, size.Y);
            Color[] data = new Color[size.X * size.Y];
            for (int i = 0; i < data.Length; ++i)
                data[i] = Color.Chocolate;
            rectDummy.SetData(data);
        }
        private void BuildingCollisions()
        {

        }


        #endregion
    }
}
